# Always print this out before your assignment
sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets 
[6] methods   base     

other attached packages:
[1] here_1.0.1 knitr_1.36

loaded via a namespace (and not attached):
 [1] compiler_4.1.2  fastmap_1.1.0   rprojroot_2.0.2
 [4] tools_4.1.2     htmltools_0.5.2 yaml_2.2.1     
 [7] rmarkdown_2.11  xfun_0.28       digest_0.6.28  
[10] rlang_0.4.12    evaluate_0.14  
getwd()
[1] "/Users/ryanradcliff/Documents/BUS696/BROCODE_Final_Project"

# load all your libraries in this chunk 
library('tidyverse')
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.0     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library("fs")
library('here')
here() starts at /Users/ryanradcliff/Documents/BUS696
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
library('ggthemes')
library('kableExtra')

Attaching package: ‘kableExtra’

The following object is masked from ‘package:dplyr’:

    group_rows
library('pastecs')

Attaching package: ‘pastecs’

The following objects are masked from ‘package:dplyr’:

    first, last

The following object is masked from ‘package:tidyr’:

    extract
library('viridis')
Loading required package: viridisLite
library('plotly')
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library('tidyquant')
Loading required package: PerformanceAnalytics
Loading required package: xts
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric


Attaching package: ‘xts’

The following objects are masked from ‘package:pastecs’:

    first, last

The following objects are masked from ‘package:dplyr’:

    first, last


Attaching package: ‘PerformanceAnalytics’

The following object is masked from ‘package:graphics’:

    legend

Loading required package: quantmod
Loading required package: TTR
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
══ Need to Learn tidyquant? ══════════════════════════════════════════
Business Science offers a 1-hour course - Learning Lab #9: Performance Analysis & Portfolio Optimization with tidyquant!
</> Learn more at: https://university.business-science.io/p/learning-labs-pro </>
library('scales')

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))

#conferral dates
dataclean <- dataclean %>%
  
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years"))))

#major gift 
dataclean <- 
  dataclean %>% 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1")))


#splitting up the age into ranges and creating category for easy visualization 
dataclean <- dataclean %>%
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA))))))))))


#seeing what we have
table(dataclean$age_range)

10 < 20 years old 20 < 30 years old 30 < 40 years old 
             3990             24560             21034 
40 < 50 years old 50 < 60 years old 60 < 70 years old 
            16857             20748             18258 
70 < 80 years old 80 < 90 years old     90+ years old 
            12242              5986              6632 
#50-60 is the most common age range 

#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA))))))))))))))))))))))))))))))))))))))))))

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region))))))))))))))))))))))))


# code nor cal region as all others in CA not already defined

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))


#Removing Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E))

#slry range 
dataclean <- dataclean %>%
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records

dataclean <- unique(dataclean)

#n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),

    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

DonorSegment Analysis

#grouping by donorsegment and analyzing 
dataclean %>%
  group_by(Donor.Segment) %>%
  summarise(Count = length(Donor.Segment),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  #added scales package to have the values show in dollar 
  mutate(mean_total_giv = dollar(mean_total_giv)) %>%
  kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93
NA
NA

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

NA
NA
NA

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

NA
NA
NA

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)
Rows: 10
Columns: 4
$ age_range                     <chr> "10 < 20 years old", "20 < 30 …
$ avg_giving                    <dbl> 0.4449699, 28.2733731, 391.223…
$ med_giving                    <dbl> 0, 0, 0, 0, 0, 0, 0, 10, 15, 0
$ amount_of_people_in_age_range <int> 3990, 24553, 21021, 16837, 207…

Part 2

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1))

NA
NA

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))

NA
NA

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)

NA
NA

Giving by gender


#remove NAs U X 
q <- ggplot(dataclean)
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = sex_simple), 
  fun.y = "mean", geom = "bar")


summary(dataclean$sex_simple)
     F      M      U      X   NA's 
120781 108190   3683      7  90339 

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))
NA
NA
NA

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
Donor's State Count
CA 176487
WA 7957
TX 7266
NY 5659
CO 5073
AZ 4925
OR 4612
FL 4111
IL 3681
HI 3394
PA 2904
OH 2754
NV 2715
MI 2523
MA 2473
NJ 2311
VA 2158
NC 2085
GA 2044
MO 1889
MN 1732
MD 1488
TN 1443
IN 1417
CT 1380
WI 1330
UT 1173
OK 1151
AL 1120
LA 1110
ID 1096
SC 1076
KY 1032
KS 1027
NM 981
IA 880
NA
NA
NA
NA
NA
NA

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% 3.8
50% 25.0
75% 100.0
90% 500.0
99% 15000.0
NA
NA
NA
NA

Modeling for you

Split data

#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))

dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving))


library("rsample")

data_split <- initial_split(dataclean, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- dataclean %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x',
which will replace the existing scale.
ggplotly(p)
  
p


ggplot(data = dataclean, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

NA
NA
NA

Another Histogram


dataclean %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
dataclean %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
dataclean %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

ggplot(data = data_train, aes(x = Age, y = log(Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~No_of_Children) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Title")
`geom_smooth()` using formula 'y ~ x'

MORE MODELS

Big logistic model


# Set family to binomial to set logistic function
# Run the model on the training set

donor_logit1 <-
  glm(hh.lifetime.giving_fct ~ Married_simple,
      family = "binomial",
      data = data_train)

summary(donor_logit1)

Call:
glm(formula = hh.lifetime.giving_fct ~ Married_simple, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.1122  -0.6872  -0.6872   1.2440   1.7659  

Coefficients:
                Estimate Std. Error z value            Pr(>|z|)    
(Intercept)    -1.323203   0.005906  -224.1 <0.0000000000000002 ***
Married_simple  1.167887   0.009628   121.3 <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 288365  on 242249  degrees of freedom
Residual deviance: 273662  on 242248  degrees of freedom
AIC: 273666

Number of Fisher Scoring iterations: 4
donor_logit2 <-
  glm(hh.lifetime.giving_fct ~ No_of_Children,
      family = "binomial",
      data = data_train)

summary(donor_logit2)

Call:
glm(formula = hh.lifetime.giving_fct ~ No_of_Children, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-0.9083  -0.8000  -0.8000   1.5411   1.6094  

Coefficients:
                Estimate Std. Error z value            Pr(>|z|)    
(Intercept)    -0.975131   0.005212 -187.10 <0.0000000000000002 ***
No_of_Children  0.151469   0.009049   16.74 <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 288365  on 242249  degrees of freedom
Residual deviance: 288089  on 242248  degrees of freedom
AIC: 288093

Number of Fisher Scoring iterations: 4
#summary(data_train$major_gifter)

donor_logit3 <-
  glm(major_gifter ~ Married_simple + No_of_Children + donorseg_simple + Assignment_flag + Total.Giving.Years,
      family = "binomial",
      data = data_train)

summary(donor_logit3)

Call:
glm(formula = major_gifter ~ Married_simple + No_of_Children + 
    donorseg_simple + Assignment_flag + Total.Giving.Years, family = "binomial", 
    data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.4932  -0.1410  -0.1220  -0.0873   3.5040  

Coefficients:
                             Estimate Std. Error z value
(Intercept)                  -4.08066    0.24408 -16.718
Married_simple               -1.23973    0.08749 -14.170
No_of_Children                0.71530    0.05765  12.407
donorseg_simpleCurrent Donor -0.04313    0.24679  -0.175
donorseg_simpleLapsed Donor  -0.60244    0.25434  -2.369
donorseg_simpleLapsing Donor -0.40470    0.26802  -1.510
donorseg_simpleLost Donor    -0.96219    0.24415  -3.941
Assignment_flag               1.19911    0.11744  10.210
Total.Giving.Years            0.14559    0.00441  33.013
                                         Pr(>|z|)    
(Intercept)                  < 0.0000000000000002 ***
Married_simple               < 0.0000000000000002 ***
No_of_Children               < 0.0000000000000002 ***
donorseg_simpleCurrent Donor               0.8613    
donorseg_simpleLapsed Donor                0.0179 *  
donorseg_simpleLapsing Donor               0.1311    
donorseg_simpleLost Donor               0.0000811 ***
Assignment_flag              < 0.0000000000000002 ***
Total.Giving.Years           < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9764.4  on 68404  degrees of freedom
Residual deviance: 7544.5  on 68396  degrees of freedom
  (173845 observations deleted due to missingness)
AIC: 7562.5

Number of Fisher Scoring iterations: 8
exp(donor_logit3$coefficients)
                 (Intercept)               Married_simple 
                  0.01689628                   0.28946282 
              No_of_Children donorseg_simpleCurrent Donor 
                  2.04479757                   0.95778285 
 donorseg_simpleLapsed Donor donorseg_simpleLapsing Donor 
                  0.54747539                   0.66717987 
   donorseg_simpleLost Donor              Assignment_flag 
                  0.38205405                   3.31714962 
          Total.Giving.Years 
                  1.15672540 
#training predictions for in sample preds 
preds_train <- predict(donor_logit3, newdata = data_train, type = "response") 

#test predicts for OOS (out of sample)
preds_test <- predict(donor_logit3, newdata = data_test, type = "response")

head(preds_train)
     196454      181024       64789       23864       86886 
         NA 0.003806297          NA          NA          NA 
     188620 
         NA 
head(preds_test)
          1           6          17          18          21 
0.047220650 0.764137284 0.931364886 0.001865082 0.040099014 
         27 
0.188797205 
results_train <- data.frame(
  `truth` = data_train   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_train,
  `type` = rep("train",length(preds_train))
)

results_test <- data.frame(
  `truth` = data_test   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_test,
  `type` = rep("test",length(preds_test))
)

results <- bind_rows(results_train,results_test)

dim(results_train)
[1] 242250      3
dim(results_test)
[1] 80750     3
dim(results)
[1] 323000      3
library('plotROC')

p_plot <-
  ggplot(results,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 2.5,
           #Took the labelsize down to avoid cutoff
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 #We removed some of the cutoffs to avoid the mashup near the origin.

  #Changed the theme to avoid cutoff plot values.
  theme_classic(base_size = 14) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")
print(p_plot) 



p_train <-
  ggplot(results_train,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

p_test <-
  ggplot(results_test,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

#Calculating AUC of both
print(calc_auc(p_train)$AUC)
[1] 0.8823867
print(calc_auc(p_test)$AUC)
[1] 0.8713205

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
print(ridge_fit1$lambda.min)

print(ridge_fit1$lambda.1se)

LASSO

coef(lasso_fit)
36 x 1 sparse Matrix of class "dgCMatrix"
                                                                       s1
(Intercept)                                                      5406.897
jobtitle_simpleAttorney                                             .    
jobtitle_simpleOwner                                                .    
jobtitle_simplePresident                                            .    
jobtitle_simpleTeacher                                              .    
jobtitle_simpleUnknown Position                                     .    
jobtitle_simpleOther                                                .    
deg1_simpleBachelor of Arts                                         .    
deg1_simpleBachelor of Fine Arts                                    .    
deg1_simpleBachelor of Science                                      .    
deg1_simpleMaster of Arts                                           .    
deg1_simpleNon Degree - Undergraduate                               .    
deg1_simpleOther                                                    .    
school1_simpleCollege of Health and Behavioral Sciences             .    
school1_simpleDonna Ford Attallah College of Educational Studies    .    
school1_simpleGeorge L. Argyros School of Business and Economics    .    
school1_simpleLawrence and Kristina Dodge Coll of Film & Media      .    
school1_simpleWilkinson Coll of Arts  Humanities  & Soc Sciences    .    
school1_simpleOther                                                 .    
hhfirstgift_simpleChapman Annual Scholarship Fund                   .    
hhfirstgift_simpleChapman Fund                                      .    
hhfirstgift_simpleJog-A-Thon                                        .    
hhfirstgift_simplePhonathon                                         .    
hhfirstgift_simplePre-SRN Conversion Gift History                   .    
hhfirstgift_simpleOther                                             .    
maj1_simpleBusiness Administration BS                               .    
maj1_simpleEducation                                                .    
maj1_simpleLaw (Full-Time)                                          .    
maj1_simpleUndecided - UG                                           .    
maj1_simpleUnknown Major                                            .    
maj1_simpleOther                                                    .    
donorseg_simpleAt-Risk Donor                                        .    
donorseg_simpleCurrent Donor                                        .    
donorseg_simpleLapsed Donor                                         .    
donorseg_simpleLapsing Donor                                        .    
donorseg_simpleLost Donor                                           .    

#enet_mod <- cva.glmnet(dependent ~ indy1 + indy2,
#                       data = data,
#                       alpha = seq(0,1, by = 0.1))

#print(enet_mod)
#plot(enet_mod)

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")

get_alpha <- function(fit) {
  alpha <- fit$alpha
  error <- sapply(fit$modlist, 
                  function(mod) {min(mod$cvm)})
  alpha[which.min(error)]
}

get_model_params <- function(fit) {
  alpha <- fit$alpha
  lambdaMin <- sapply(fit$modlist, `[[`, "lambda.min")
  lambdaSE <- sapply(fit$modlist, `[[`, "lambda.1se")
  error <- sapply(fit$modlist, function(mod) {min(mod$cvm)})
  best <- which.min(error)
  data.frame(alpha = alpha[best], lambdaMin = lambdaMin[best],
             lambdaSE = lambdaSE[best], eror = error[best])
}

best_alpha <- get_alpha(enet_mod)
print(best_alpha)
get_model_params(enet_mod)

best_mod <- enet_mod$modlist[[which(enet_mod$alpha == best_alpha)]]

print(best_mod)

Ridges plot - could be useful for plotting donations vs donor segment


library('ggridges')

summary(data_train$variable)

ggplot(data_train, aes(x = HH.Lifetime.Giving, y = donorseg_fct)) + geom_density_ridges(rel_min_height = 0.005) + xlim(c(0, 400)) + 
      ggtitle("HH Lifetime Giving by Donor Segment")

library('corrplot')

#removing ID zip and nonnumeric 
corrplot_data <- dataclean[-c(1:47,55:56,58:130)]

#Convert from character to numeric data type
convert_fac2num <- function(x){
  as.numeric(as.factor(x))
}

corrplot_data <- mutate_at(corrplot_data,
                     .vars = c(1:8),
                     .funs = convert_fac2num)
#making a matrix
cd_cor <- cor(corrplot_data)

#creating correlation
col <- colorRampPalette(c("#BB4400", "#EE9990", 
  "#FFFFFF", "#77AAEE", "#4477BB"))
corrplot(cd_cor, method="color", col=col(100),
  type="lower", addCoef.col = "black",
  tl.pos="lt", tl.col="black", 
  tl.cex=0.7, tl.srt=45, 
  number.cex=0.7,
  diag=FALSE)

Random Forest


library('randomForest')

rf_fit_donor <- randomForest(dependent ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )

print(rf_fit_donor)

varImpPlot(rf_fit_donor, sort = TRUE, 
           n.var = 5,
           type = 2, class = NULL, scale = TRUE, 
           main = deparse(substitute(rf_fit_donor)))

library('randomForestExplainer')

plot_min_depth_distribution(
  rf_fit_donor,
  k = 10,
  min_no_of_trees = 0,
  mean_sample = "top_trees",
  mean_scale = FALSE,
  mean_round = 2,
  main = "Distribution of minimal depth and its mean"
)
LS0tCnRpdGxlOiAiQlJPQ09ERSBTdW1tYXJ5IFN0YXRpc3RpY3MiCmF1dGhvcjogIkFhcm9uIFdpbGxpcywgQ2Fubm9uIEJyb29rZSwgSm9zaHVhIEhlbmRlcnNvbiwgUnlhbiBSYWRjbGlmZiIKc3VidGl0bGU6IEJVUzY5NiBGaW5hbCBQcm9qZWN0IHYxMgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCiMgUGxlYXNlIGxlYXZlIHRoaXMgY29kZSBjaHVuayBhcyBpcy4gSXQgbWFrZXMgc29tZSBzbGlnaHQgZm9ybWF0dGluZyBjaGFuZ2VzIHRvIGFsdGVyIHRoZSBvdXRwdXQgdG8gYmUgbW9yZSBhZXN0aGV0aWNhbGx5IHBsZWFzaW5nLiAKCmxpYnJhcnkoJ2tuaXRyJykKCgojIENoYW5nZSB0aGUgbnVtYmVyIGluIHNldCBzZWVkIHRvIHlvdXIgb3duIGZhdm9yaXRlIG51bWJlcgpzZXQuc2VlZCgxODE4KQpvcHRpb25zKHdpZHRoPTcwKQpvcHRpb25zKHNjaXBlbj05OSkKCgojIHRoaXMgc2V0cyB0ZXh0IG91dHB1dHRlZCBpbiBjb2RlIGNodW5rcyB0byBzbWFsbApvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC53cmFwPTUwKSx0aWR5PVRSVUUsIHNpemUgPSAidnNtYWxsIikgIApvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgIyAiY2FjaGluZyIgc3RvcmVzIG9iamVjdHMgaW4gY29kZSBjaHVua3MgYW5kIG9ubHkgcmV3cml0ZXMgaWYgeW91IGNoYW5nZSB0aGluZ3MKICAgICAgICAgICAgICAgY2FjaGUgPSBUUlVFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIyBhdXRvbWF0aWNhbGx5IGRvd25sb2FkcyBkZXBlbmRlbmN5IGZpbGVzCiAgICAgICAgICAgICAgIGF1dG9kZXAgPSBUUlVFLAogICAgICAgICAgICAgICAjIAogICAgICAgICAgICAgICBjYWNoZS5jb21tZW50cyA9IEZBTFNFLAogICAgICAgICAgICAgICAjIAogICAgICAgICAgICAgICBjb2xsYXBzZSA9IFRSVUUsCiAgICAgICAgICAgICAgICMgY2hhbmdlIGZpZy53aWR0aCBhbmQgZmlnLmhlaWdodCB0byBjaGFuZ2UgdGhlIGNvZGUgaGVpZ2h0IGFuZCB3aWR0aCBieSBkZWZhdWx0CiAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDUuNSwgIAogICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNC41LAogICAgICAgICAgICAgICBmaWcuYWxpZ249J2NlbnRlcicpCgoKYGBgCgpgYGB7ciBzZXR1cC0yfQoKIyBBbHdheXMgcHJpbnQgdGhpcyBvdXQgYmVmb3JlIHlvdXIgYXNzaWdubWVudApzZXNzaW9uSW5mbygpCmdldHdkKCkKCmBgYAoKCjwhLS0gIyMjIHN0YXJ0IGFuc3dlcmluZyB5b3VyIHByb2JsZW0gc2V0IGhlcmUgLS0+CjwhLS0gWW91IG1heSBleHBvcnQgeW91ciBob21ld29yayBpbiBlaXRoZXIgaHRtbCBvciBwZGYsIHdpdGggdGhlIGZvcm1lciB1c3VhbGx5IGJlaW5nIGVhc2llci4gCiAgICAgVG8gZXhwb3J0IG9yIGNvbXBpbGUgeW91ciBSbWQgZmlsZTogY2xpY2sgYWJvdmUgb24gJ0tuaXQnIHRoZW4gJ0tuaXQgdG8gSFRNTCcgLS0+CjwhLS0gQmUgc3VyZSB0byBzdWJtaXQgYm90aCB5b3VyIC5SbWQgZmlsZSBhbmQgdGhlIGNvbXBpbGVkIC5odG1sIG9yIC5wZGYgZmlsZSBmb3IgZnVsbCBjcmVkaXQgLS0+CgoKYGBge3Igc2V0dXAtM30KCiMgbG9hZCBhbGwgeW91ciBsaWJyYXJpZXMgaW4gdGhpcyBjaHVuayAKbGlicmFyeSgndGlkeXZlcnNlJykKbGlicmFyeSgiZnMiKQpsaWJyYXJ5KCdoZXJlJykKbGlicmFyeSgnZHBseXInKQpsaWJyYXJ5KCd0aWR5dmVyc2UnKQpsaWJyYXJ5KCdnZ3Bsb3QyJykKbGlicmFyeSgnZ2dyZXBlbCcpCmxpYnJhcnkoJ2dndGhlbWVzJykKbGlicmFyeSgnZm9yY2F0cycpCmxpYnJhcnkoJ3JzYW1wbGUnKQpsaWJyYXJ5KCdsdWJyaWRhdGUnKQpsaWJyYXJ5KCdnZ3RoZW1lcycpCmxpYnJhcnkoJ2thYmxlRXh0cmEnKQpsaWJyYXJ5KCdwYXN0ZWNzJykKbGlicmFyeSgndmlyaWRpcycpCmxpYnJhcnkoJ3Bsb3RseScpCmxpYnJhcnkoJ3RpZHlxdWFudCcpCmxpYnJhcnkoJ3NjYWxlcycpCgoKIyBub3RlLCBkbyBub3QgcnVuIGluc3RhbGwucGFja2FnZXMoKSBpbnNpZGUgYSBjb2RlIGNodW5rLiBpbnN0YWxsIHRoZW0gaW4gdGhlIGNvbnNvbGUgb3V0c2lkZSBvZiBhIGNvZGUgY2h1bmsuIAoKYGBgCgoKCiMjIFBhcnQgMSAtIEZpbmFsIFByb2plY3QgQ2xlYW5pbmcgYW5kIFN1bW1hcnkgU3RhdGlzdGljcyAKCjFhKSBMb2FkaW5nIGRhdGEKCmBgYHtyfQoKI1JlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGRvaW5nIG1pbm9yIGluaXRpYWwgY2xlYW5pbmcgaW4gdGhlIGZ1bmN0aW9uIGNhbGwKI1JlcHJvZHVjaWJsZSBkYXRhIGFuYWx5c2lzIHNob3VsZCBhdm9pZCBhbGwgYXV0b21hdGljIHN0cmluZyB0byBmYWN0b3IgY29udmVyc2lvbnMuCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIAojbmEuc3RyaW5ncyBpcyBhIHN1YnN0aXR1dGlvbiBzbyBhbGwgdGhhdCBoYXZlICIiIHdpbGwgPSBuYQpkYXRhIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiZG9ub3JfZGF0YS5jc3YiKSwKICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSAiIikKCgoKYGBgCgoKMWIpIEZpeGluZyB0aGUgd29ua3kgRE9CICYgRGF0YSBjbGVhbnVwCgpgYGB7cn0KCiMoQmlydGhkYXRlIGFuZCBBZ2UsIElEIGFzIGEgbnVtYmVyKWFkZGluZyBET0IgKEFnZS9TcG91c2UgQWdlKSBpbiB5ZWFycyBjb2x1bW5zIGFuZCBhZGRpbmcgdHdvIGZpZWxkcyBmb3IgYXNzaWdubWVudCBhbmQgbnVtYmVyIG9mIGNoaWxkcmVuCmRhdGFjbGVhbiA8LSBkYXRhICU+JQogIG11dGF0ZShCaXJ0aGRhdGUgPSBpZmVsc2UoQmlydGhkYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIEJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShCaXJ0aGRhdGUgPSBtZHkoQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKEFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEJpcnRoZGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICBtdXRhdGUoU3BvdXNlLkJpcnRoZGF0ZSA9IGlmZWxzZShTcG91c2UuQmlydGhkYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIFNwb3VzZS5CaXJ0aGRhdGUpKSAlPiUKICBtdXRhdGUoU3BvdXNlLkJpcnRoZGF0ZSA9IG1keShTcG91c2UuQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBTcG91c2UuQmlydGhkYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIG11dGF0ZShJRCA9IGFzLm51bWVyaWMoSUQpKSAlPiUgCiAgbXV0YXRlKEFzc2lnbm1lbnRfZmxhZyA9IGlmZWxzZShpcy5uYShBc3NpZ25tZW50Lk51bWJlciksIDAsMSkpICU+JSAKICBtdXRhdGUoIE5vX29mX0NoaWxkcmVuID0gaWZlbHNlKGlzLm5hKENoaWxkLjEuSUQpLDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoQ2hpbGQuMi5JRCksMSwyKSkpCgojY29uZmVycmFsIGRhdGVzCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4xKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBtZHkoQ29uZmVycmFsLkRhdGUuMSkpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjEsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMiA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4yKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBtZHkoQ29uZmVycmFsLkRhdGUuMikpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjIsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBpZmVsc2UoTGFzdC5Db250YWN0LkJ5LkFueW9uZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBMYXN0LkNvbnRhY3QuQnkuQW55b25lKSkgJT4lCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBtZHkoTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gTGFzdC5Db250YWN0LkJ5LkFueW9uZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICAKIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBpZmVsc2UoSEguRmlyc3QuR2lmdC5EYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIEhILkZpcnN0LkdpZnQuRGF0ZSkpICU+JQogIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBtZHkoSEguRmlyc3QuR2lmdC5EYXRlKSkgJT4lCm11dGF0ZShISC5GaXJzdC5HaWZ0LkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEhILkZpcnN0LkdpZnQuRGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKQoKI21ham9yIGdpZnQgCmRhdGFjbGVhbiA8LSAKICBkYXRhY2xlYW4gJT4lIAogIG11dGF0ZShtYWpvcl9naWZ0ZXIgPSBpZmVsc2UoTGlmZXRpbWUuR2l2aW5nID4gNTAwMDAsIDEsMCkgJT4lIGZhY3RvciguLCBsZXZlbHMgPSBjKCIwIiwiMSIpKSkKCgojc3BsaXR0aW5nIHVwIHRoZSBhZ2UgaW50byByYW5nZXMgYW5kIGNyZWF0aW5nIGNhdGVnb3J5IGZvciBlYXN5IHZpc3VhbGl6YXRpb24gCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKGFnZV9yYW5nZSA9IAogICAgaWZlbHNlKEFnZSAlaW4lIDEwOjE5LCAiMTAgPCAyMCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDIwOjI5LCAiMjAgPCAzMCB5ZWFycyBvbGQiLCAKICAgIGlmZWxzZShBZ2UgJWluJSAzMDozOSwgIjMwIDwgNDAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA0MDo0OSwgIjQwIDwgNTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA1MDo1OSwgIjUwIDwgNjAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA2MDo2OSwgIjYwIDwgNzAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA3MDo3OSwgIjcwIDwgODAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA4MDo4OSwgIjgwIDwgOTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA5MDoxMjAsICI5MCsgeWVhcnMgb2xkIiwKICAgIE5BKSkpKSkpKSkpKQoKCiNzZWVpbmcgd2hhdCB3ZSBoYXZlCnRhYmxlKGRhdGFjbGVhbiRhZ2VfcmFuZ2UpCiM1MC02MCBpcyB0aGUgbW9zdCBjb21tb24gYWdlIHJhbmdlIAoKI2NyZWF0aW5nIGEgcmVnaW9uIGNvbHVtbiB1c2luZyB0aGUgY291bnR5IGRhdGEgYW5kIHRoZSBPTUIgTVNBIChNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSkgZGVmaW5pdGlvbnMKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHJlZ2lvbiA9IAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEx1aXMgT2Jpc3BvIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiS2VybiIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBCZXJuYXJkaW5vIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQmFyYmFyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlZlbnR1cmEiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJMb3MgQW5nZWxlcyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk9yYW5nZSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJpdmVyc2lkZSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBEaWVnbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkltcGVyaWFsIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiS2luZyIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTbm9ob21pc2giICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGllcmNlIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNsYWNrYW1hcyIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ29sdW1iaWEiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk11bHRub21haCIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiV2FzaGluZ3RvbiIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiWWFtaGlsbCIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xhcmsiICYgU3RhdGUgPT0gIldBIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNrYW1hbmlhIiAmIFN0YXRlID09ICJXQSIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJEZW52ZXIiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJBcmFwYWhvZSIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkplZmZlcnNvbiIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkFkYW1zIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiRG91Z2xhcyIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJyb29tZmllbGQiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsICAgIAogICAgaWZlbHNlKENvdW50eSA9PSAiRWxiZXJ0IiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGFyayIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNsZWFyIENyZWVrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQWxhbWVkYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ29udHJhIENvc3RhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNYXJpbiIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTW9udGVyZXkiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk5hcGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBCZW5pdG8iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBGcmFuY2lzY28iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBNYXRlbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQ2xhcmEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbnRhIENydXoiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNvbGFubyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU29ub21hIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICAgICAgICAgTkEpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHJlZ2lvbiA9IAogICAgaWZlbHNlKENvdW50eSA9PSAiS2luZ3MiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlF1ZWVucyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTmV3IFlvcmsiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJyb254IiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJSaWNobW9uZCIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiV2VzdGNoZXN0ZXIiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJlcmdlbiIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSHVkc29uIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQYXNzYWljIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQdXRuYW0iICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJvY2tsYW5kIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTdWZmb2xrIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJOYXNzYXUiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1pZGRsZXNleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTW9ubW91dGgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk9jZWFuIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb21lcnNldCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiRXNzZXgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlVuaW9uIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb3JyaXMiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlN1c3NleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSHVudGVyZG9uIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWtlIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICByZWdpb24pKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkKCgojIGNvZGUgbm9yIGNhbCByZWdpb24gYXMgYWxsIG90aGVycyBpbiBDQSBub3QgYWxyZWFkeSBkZWZpbmVkCgpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogIG11dGF0ZShyZWdpb24gPSAKICAgIGlmZWxzZShTdGF0ZSA9PSAiQ0EiICYgaXMubmEocmVnaW9uKSA9PSBUUlVFLCAiTm9yIENhbCIsIHJlZ2lvbikpCgoKI1JlbW92aW5nIENvbHVtbnMgdGhhdCBwcm92aWRlIG5vIGJlbmVmaXQgCgpkYXRhY2xlYW4gPC0gc3Vic2V0KGRhdGFjbGVhbixzZWxlY3QgPSAtYyhBc3NpZ25tZW50Lk51bWJlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuaGFzLkhpc3RvcmljYWwuTW5ncgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN1ZmZpeAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuUm9sZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuVGl0bGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlN0YXR1cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN0cmF0ZWd5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvZ3Jlc3MuTGV2ZWwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lkdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5DYXRlZ29yeQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEZ1bmRpbmcuTWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEV4cGVjdGVkLkJvb2suRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxRdWFsaWZpY2F0aW9uLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGFyZC5HaWZ0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFNvZnQuQ3JlZGl0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFRvdGFsLkFzc2lnbm1lbnQuR2lmdHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTm8ub2YuUGxlZGdlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb3Bvc2FsLk5vdGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhILkxpZmUuU3BvdXNlLkNyZWRpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxMYXN0LkNvbnRhY3QuQnkuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxYLi5vZi5Db250YWN0cy5CeS5NYW5hZ2VyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLERvbm9yU2VhcmNoLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLGlXYXZlLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFdlYWx0aEVuZ2luZS5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQaGlsYW50aHJvcGljLkNvbW1pdG1lbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKI2NsZWFuaW5nIHVwIHppcCBjb2RlcyByZW1vdmluZyAtNCBhZnRlciAKZGF0YWNsZWFuJFppcCA8LSBnc3ViKGRhdGFjbGVhbiRaaXAsIHBhdHRlcm49Ii0uKiIsIHJlcGxhY2VtZW50ID0gIiIpCgojYWRkaW5nIHppcCBjb2RlIGRhdGEgYW5kIGNvbHVtbiAKemlwIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiU2FsYXJ5X1ppcGNvZGUuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgoKI2FkZGluZyB6aXAgc2FsYXJ5IGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoemlwY29kZV9zbHJ5ID0gVkxPT0tVUChaaXAsIHppcCwgTkFNRSwgUzE5MDJfQzAzXzAwMkUpKQoKI3NscnkgcmFuZ2UgCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHppcHNscnlfcmFuZ2UgPSAKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxMDAwMDo4OTk5OSwgIjkwSy05OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDkwMDAwOjk5OTk5LCAiOTBLLTk5SyIsCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDAwOjE0OTk5OSwgIjEwMEstMTQ5SyIsIAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDE1MDAwMDoxOTk5OTksICIxNTBLLTE5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDIwMDAwMDoyNDk5OTksICIyMDBLLTI0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDI1MDAwMDoyOTk5OTksICIyNTBLLTI5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDMwMDAwMDozNDk5OTksICIzMDBLLTM0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDM1MDAwMDozOTk5OTksICIzNTBLLTM5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDQwMDAwMDo0OTk5OTksICI0MDBLLTQ5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDUwMDAwMDo5OTk5OTksICI1MDBLLTk5OUsiLAogICAgTkEpKSkpKSkpKSkpKQoKc3VtKGlzLm5hKGRhdGFjbGVhbiR6aXBjb2RlX3NscnkpKQoKI2FkZGluZyBzY2hvbGFyc2hpcCBkYXRhICh5L24pCnNjaGxyIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAic2Nob2xhcnNoaXAuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgojYWRkaW5nIHNjaG9sYXJzaGlwIGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoc2Nob2xhcnNoaXAgPSBWTE9PS1VQKElELCBzY2hsciwgSUQsIFNDSE9MQVJTSElQKSkgCgojcmVwbGFjaW5nIE5BIHdpdGggMCAKIGRhdGFjbGVhbiRzY2hvbGFyc2hpcCA8LSByZXBsYWNlX25hKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCwnMCcpCiAKI3JlcGxhY2luZyBZIHdpdGggMSAKZGF0YWNsZWFuJHNjaG9sYXJzaGlwPC1pZmVsc2UoZGF0YWNsZWFuJHNjaG9sYXJzaGlwPT0iWSIsMSwwKQoKI2NoZWNraW5nIGhvdyBtYW55IGFyZSBOCnRhYmxlKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCkKCgojY2hlY2tpbmcgYW5kIGRlbGV0aW5nIHNjaG9sYXJzaGlwIGNvbHVtbiAKY2xhc3MoZGF0YWNsZWFuJHNjaGxyX2ZjdCkKZGF0YWNsZWFuID0gc3Vic2V0KGRhdGFjbGVhbiwgc2VsZWN0ID0gLWMoc2Nob2xhcnNoaXApKQogIAojY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMgTiA+MSBpbmRpY2F0ZXMgYSByZWNvcmRzIHZhbHVlcyBhcmUgaW4gdGhlIGZpbGUgdHdpY2UgCmRhdGFjbGVhbiAlPiUgZ3JvdXBfYnkoSUQpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpCgojcmVtb3ZpbmcgZHVwbGljYXRlZCByZWNvcmRzCgpkYXRhY2xlYW4gPC0gdW5pcXVlKGRhdGFjbGVhbikKCiNuID0gMSBubyBJRCB3aXRoIG11bHRpcGxlIHJlY29yZHMgY2xlYW5lZCBvZiBkdXBlcwpkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKYGBgCgoKMWQgQ3JlYXRpbmcgbWFueSBtYW55IGZhY3RvciB2YXJpYWJsZXMKCmBgYHtyfQoKZGF0YWNsZWFuIDwtIAogIGRhdGFjbGVhbiAlPiUgCiAgI1NFWAogIG11dGF0ZShzZXhfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNleCksCnNleF9zaW1wbGUgPSAKICAgIGZjdF9sdW1wX24oU2V4LCBuID0gNCksCiNNQVJSSUVECm1hcnJpZWRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1hcnJpZWQpLAogICNET05PUiBTRUdNRU5UCiAgZG9ub3JzZWdfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERvbm9yLlNlZ21lbnQpLAogICAgICAgICBkb25vcnNlZ19zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKERvbm9yLlNlZ21lbnQsIG4gPSA0KSwKICAjQ09OVEFDVCBSVUxFCiAgICAgICAgIGNvbnRhY3RfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENvbnRhY3QuUnVsZXMpLAogICAgICAgICBjb250YWN0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ29udGFjdC5SdWxlcywgbiA9IDQpLAogICNTUE9VU0UgTUFJTAogICAgICAgICBzcG9tYWlsX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTcG91c2UuTWFpbC5SdWxlcyksCiAgICAgICAgIHNwb21haWxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTcG91c2UuTWFpbC5SdWxlcywgbiA9IDQpLAogICNKT0IgVElUTEUKICAgICAgICAgam9idGl0bGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEpvYi5UaXRsZSksCiAgICAgICAgIGpvYnRpdGxlX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSm9iLlRpdGxlLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDEKICAgICAgICAgZGVnMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMSksCiAgICAgICAgIGRlZzFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4xLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDIKICAgICAgICAgZGVnMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMiksCiAgICAgICAgIGRlZzJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4yLCBuID0gNSksCiAgI01BSk9SIDEKICAgICAgICAgbWFqMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMSksCiAgICAgICAgIG1hajFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4xLCBuID0gNSksCiAgI01BSk9SIDIKICAgICAgICAgbWFqMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMiksCiAgICAgICAgIG1hajJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4yLCBuID0gNSksCiAgI01JTk9SIDEKICAgICAgICAgbWluMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMSksCiAgICAgICAgIG1pbjFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4xLCBuID0gNSksCiAgI01JTk9SIDIKICAgICAgICAgbWluMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMiksCiAgICAgICAgIG1pbjJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4yLCBuID0gNSksCiAgI1NDSE9PTCAxCiAgICAgICAgIHNjaG9vbDFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4xKSwKICAgICAgICAgc2Nob29sMV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4xLCBuID0gNSksCiAgI1NDSE9PTCAyCiAgICAgICAgIHNjaG9vbDJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4yKSwKICAgICAgICAgc2Nob29sMl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4yLCBuID0gNSksCiAgI0lOU1RJVFVUSU9OIFRZUEUKICAgICAgICAgaW5zdHR5cGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEluc3RpdHV0aW9uLlR5cGUpLAogICAgICAgICBpbnN0dHlwZV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEluc3RpdHV0aW9uLlR5cGUsIG4gPSA1KSwKICAjRVhUUkFDVVJSSUNVTEFSCiAgICAgICAgIGV4dHJhX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShFeHRyYWN1cnJpY3VsYXIpLAogICAgICAgICBleHRyYV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEV4dHJhY3VycmljdWxhciwgbiA9IDUpLAogICNISCBGSVJTVCBHSUZUIEZVTkQKICAgICAgICAgaGhmaXJzdGdpZnRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEhILkZpcnN0LkdpZnQuRnVuZCksCiAgICAgICAgIGhoZmlyc3RnaWZ0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSEguRmlyc3QuR2lmdC5GdW5kLCBuID0gNSksCiNDSElMRCAxIEVOUk9MTCBTVEFUVVMKICAgICAgICAgY2gxX2Vucm9sbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5FbnJvbGwuU3RhdHVzKSwKICAgICAgICAgY2gxX2Vucm9sbF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRW5yb2xsLlN0YXR1cywgbiA9IDQpLAojQ0hJTEQgMSBNQUpPUgogICAgICAgICBjaDFfbWFqX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLk1ham9yKSwKICAgICAgICAgY2gxX21hal9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuTWFqb3IsIG4gPSA0KSwKI0NISUxEIDEgTUlOT1IKICAgICAgICAgY2gxX21pbl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NaW5vciksCiAgICAgICAgIGNoMV9taW5fc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLk1pbm9yLCBuID0gNCksCiNDSElMRCAxIFNDSE9PTAogICAgICAgICBjaDFfc2Nob29sX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLlNjaG9vbCksCiAgICAgICAgIGNoMV9zY2hvb2xfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMSBGRUVERVIKICAgICAgICAgY2gxX2ZlZWRlcl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5GZWVkZXIuU2Nob29sKSwKICAgICAgICAgY2gxX2ZlZWRlcl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRmVlZGVyLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMiBFTlJPTEwgU1RBVFVTCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRW5yb2xsLlN0YXR1cyksCiAgICAgICAgIGNoMl9lbnJvbGxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwKI0NISUxEIDIgTUFKT1IKICAgICAgICAgY2gyX21hal9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5NYWpvciksCiAgICAgICAgIGNoMl9tYWpfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLk1ham9yLCBuID0gNCksCiNDSElMRCAyIE1JTk9SCiAgICAgICAgIGNoMl9taW5fZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuTWlub3IpLAogICAgICAgICBjaDJfbWluX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NaW5vciwgbiA9IDQpLAojQ0hJTEQgMiBTQ0hPT0wKICAgICAgICAgY2gyX3NjaG9vbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5TY2hvb2wpLAogICAgICAgICBjaDJfc2Nob29sX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDIgRkVFREVSCiAgICAgICAgIGNoMl9mZWVkZXJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRmVlZGVyLlNjaG9vbCksCiAgICAgICAgIGNoMl9mZWVkZXJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkZlZWRlci5TY2hvb2wsIG4gPSA0KSwKCiAgICApCgoKCiNjaGVja2luZyB0byBzZWUgaWYgaXRzIGEgZmFjdG9yCiNjbGFzcyhkYXRhY2xlYW4kc2V4X2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRkb25vcnNlZ19mY3QpCiNjbGFzcyhkYXRhY2xlYW4kY29udGFjdF9mY3QpCiNjbGFzcyhkYXRhY2xlYW4kc3BvbWFpbF9mY3QpCgojY2hlY2tpbmcgbGV2ZWxzCiNsZXZlbHMoZGF0YWNsZWFuJHNleF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJGRvbm9yc2VnX3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kY29udGFjdF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJHNwb21haWxfc2ltcGxlKQojbGV2ZWxzKGRhdGFjbGVhbiRoaGZpcnN0Z2lmdF9zaW1wbGUpCgojY3JlYXRpbmcgYSB0YWJsZSBhZ2FpbnN0IFNleCBjb2x1bW4gCiN0YWJsZShkYXRhY2xlYW4kc2V4X2ZjdCwgZGF0YWNsZWFuJHNleF9zaW1wbGUpCgoKYGBgCgoKCkRvbm9yU2VnbWVudCBBbmFseXNpcwoKYGBge3J9CiNncm91cGluZyBieSBkb25vcnNlZ21lbnQgYW5kIGFuYWx5emluZyAKZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KERvbm9yLlNlZ21lbnQpICU+JQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChEb25vci5TZWdtZW50KSwKICAgICAgICAgICAgbWVhbl90b3RhbF9naXYgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZykpICU+JQogIGFycmFuZ2UoLUNvdW50KSAlPiUKICBmaWx0ZXIoQ291bnQgPj0gMTAwKSAlPiUKICAjYWRkZWQgc2NhbGVzIHBhY2thZ2UgdG8gaGF2ZSB0aGUgdmFsdWVzIHNob3cgaW4gZG9sbGFyIAogIG11dGF0ZShtZWFuX3RvdGFsX2dpdiA9IGRvbGxhcihtZWFuX3RvdGFsX2dpdikpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yIFNlZ21lbnQiLCAiQ291bnQiLCAiTWVhbiBISCBMaWZldGltZSBHaXZpbmciKSwgYWxpZ249cmVwKCdjJywgMykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYAoKRmlyc3QgZ2lmdCBzaXplIApgYGB7cn0KYXEgPC0gcXVhbnRpbGUoZGF0YWNsZWFuJEhILkZpcnN0LkdpZnQuQW1vdW50LCBwcm9icyA9IGMoLjI1LC41MCwuNzUsLjksLjk5KSwgbmEucm0gPSBUUlVFKQoKYXEgPC0gYXMuZGF0YS5mcmFtZShhcSkKCmFxJGFxIDwtIGRvbGxhcihhcSRhcSkKCmFxICU+JQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYApDb25zZWN1dGl2ZSBnaXZpbmcKYGBge3J9CiNjb25zZWN1dGl2ZSB5ZWFycyBvZiBnaXZpbmcgCmRhdGFjbGVhbiAlPiUKICBmaWx0ZXIoTWF4LkNvbnNlYy5GaXNjYWwuWWVhcnMgPiAwKSAlPiUKICBnZ3Bsb3QoYWVzKE1heC5Db25zZWMuRmlzY2FsLlllYXJzKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkNvbnNlY3V0aXZlIFllYXJzIG9mIEdpdmluZyBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCwyKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMCw1MDAwKSkgCgoKCmBgYAoKTGlmZXRpbWUgZ2l2aW5nIGJhc2VkIG9uIG51bWJlciBvZiBjaGlsZHJlbiAKCmBgYHtyfQpkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA8PSAxMDAwMCkgJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA+IDApICU+JQogIG11dGF0ZShgTm9fb2ZfQ2hpbGRyZW5gID0gYXMuZmFjdG9yKGBOb19vZl9DaGlsZHJlbmApKSAlPiUKICBnZ3Bsb3QoYWVzKEhILkxpZmV0aW1lLkdpdmluZywgZmlsbCA9IGBOb19vZl9DaGlsZHJlbmApKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwLDEwMDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwMCw1MDAwKSkgKwogIGdndGl0bGUoIkdpdmluZyBkaXN0cmlidXRpb24gYW5kIG51bWJlciBvZiBjaGlsZHJlbiIpKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzAwMjg0NSIsICIjMDBjZmNjIiwgIiNmZjk5NzMiKSkKCgoKYGBgCgoKTWVhbiwgTWVkaWFuLCBhbmQgQ291bnQgb2YgR2l2aW5nIGluIEFnZSBSYW5nZXMgCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0gZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KGFnZV9yYW5nZSkgJT4lCiAgc3VtbWFyaXNlKGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVkX2dpdmluZyA9IG1lZGlhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGFtb3VudF9vZl9wZW9wbGVfaW5fYWdlX3JhbmdlID0gbigpKQoKCmdsaW1wc2UoYWdlX3JhbmdlX2dpdmluZykKCmBgYAoKCgoKCiMjIFBhcnQgMgoKMmEpIFBsb3R0aW5nIGF2ZXJhZ2UgZ2l2aW5nIGJ5IGFnZSByYW5nZSAKCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0KICBhZ2VfcmFuZ2VfZ2l2aW5nICU+JQogIG11dGF0ZShhZ2VfcmFuZ2UgPSBmYWN0b3IoYWdlX3JhbmdlKSkKCmdncGxvdChhZ2VfcmFuZ2VfZ2l2aW5nLCBhZXMoYWdlX3JhbmdlLCBhdmdfZ2l2aW5nKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTEpKQoKCmBgYAoKCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyAKICAgICAgIGdlb21fYmFyKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgCiAgbGFicyh0aXRsZSA9ICJDb3VudCBvZiBBZ2UgUmFuZ2VzIiwgeCA9ICIiLCB5ID0gIiIpCiAgCgpgYGAKCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIAoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgYWVzKGFnZV9yYW5nZSxISC5MaWZldGltZS5HaXZpbmcsZmlsbCA9IGFnZV9yYW5nZSkpICsgCiAgZ2VvbV9ib3hwbG90KAogIG91dGxpZXIuY29sb3VyID0gInJlZCIpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpCiAgCgpgYGAKCjJkKSBTcGxpdHRpbmcgYnkgYWdlIGFuZCBnZW5kZXIgCgoKYGBge3J9CgoKI2NyZWF0aW5nIGJveHBsb3RzIApkYXRhY2xlYW4gJT4lIAogIGZpbHRlcihBZ2UgPCAxMDApICU+JSAjcmVtb3ZpbmcgdGhlIHdlaXJkIG91dGxpZXJzIHRoYXQgYXJlIG92ZXIgMTAwIAogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgJT4lCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICB0aGVtZV9lY29ub21pc3QoKSArIAogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkKICAKCmBgYApHaXZpbmcgYnkgZ2VuZGVyCgoKYGBge3J9CgojcmVtb3ZlIE5BcyBVIFggCnEgPC0gZ2dwbG90KGRhdGFjbGVhbikKcSArIHN0YXRfc3VtbWFyeV9iaW4oCiAgYWVzKHkgPSBISC5MaWZldGltZS5HaXZpbmcsIHggPSBzZXhfc2ltcGxlKSwgCiAgZnVuLnkgPSAibWVhbiIsIGdlb20gPSAiYmFyIikKCnN1bW1hcnkoZGF0YWNsZWFuJHNleF9zaW1wbGUpCgpgYGAKCk1lYW4gYWdlIGJ5IGdlbmRlcgoKCmBgYHtyfQojYnJlYWtkb3duIG9mIHNleHMgCnRhbGx5KGdyb3VwX2J5KGRhdGFjbGVhbiwgU2V4KSkKCnN1bW1hcml6ZShncm91cF9ieShkYXRhY2xlYW4sIFNleCksIAogICAgICAgICAgYXZnX2dpdmluZyA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgYXZnX2FnZSA9IG1lYW4oQWdlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgbWVkX2FnZSA9IG1lZGlhbihBZ2UsIG5hLnJtID0gVFJVRSkpCgojZ3JvdXBpbmcgYnkgc2V4IGFuZCBhZ2UgcmFuZ2UgZm9yIHNsaWRlcyAKdGFsbHkoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgsIGFnZV9yYW5nZSkpCgoKCmBgYAoKCgoyZSkgRGlzdHJpYnV0aW9uIG9mIHBlb3BsZSBpbiB0aGUgc3RhdGVzIHRoYXQgdGhleSBsaXZlLgoKYGBge3J9CgogIGRhdGFjbGVhbiAlPiUKICBtdXRhdGUoU3RhdGUgPSBpZmVsc2UoU3RhdGUgPT0gIiAiLCAiTkEiLCBTdGF0ZSkpICU+JQogIGZpbHRlcihTdGF0ZSAhPSAiTkEiKSAlPiUKICBncm91cF9ieShTdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKFN0YXRlKSkgJT4lCiAgZmlsdGVyKENvdW50ID4gODAwKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAga2FibGUoY29sLm5hbWVzID0gYygiRG9ub3IncyBTdGF0ZSIsICJDb3VudCIpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKIAogIAogIAoKCmBgYAoKMmYpIExvb2tpbmcgYXQgYWxsIGRvbm9ycyBmaXJzdCBnaWZ0IGFtb3VudC4gNzUlIG1hZGUgYSBmaXJzdCBnaWZ0IG9mIDwxMDAuIAoKYGBge3J9Cgogbm9fbm9uX2Rvbm9ycyA8LSBkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKExpZmV0aW1lLkdpdmluZyAhPSAwKQogIApuZCA8LSBxdWFudGlsZShub19ub25fZG9ub3JzJEhILkZpcnN0LkdpZnQuQW1vdW50LCBwcm9icyA9IGMoLjI1LC41MCwuNzUsLjksLjk5KSwgbmEucm0gPSBUUlVFKQoKbmQgPC0gYXMuZGF0YS5mcmFtZShuZCkKCm5kICU+JQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKICAKCgpgYGAKCgoKIyMgTW9kZWxpbmcgZm9yIHlvdSAKClNwbGl0IGRhdGEKCmBgYCB7cn0KI2NvbnZlcnRpbmcgbWFycmllZCBZIGFuZCBOIHRvIDEgYW5kIDAgCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgICAgIG11dGF0ZShNYXJyaWVkX3NpbXBsZSA9IGlmZWxzZShNYXJyaWVkID09ICJOIiwwLDEpKQoKZGF0YWNsZWFuIDwtIGRhdGFjbGVhbiAlPiUKICBtdXRhdGUoaGgubGlmZXRpbWUuZ2l2aW5nX2ZjdCA9IGFzLmZhY3RvcihISC5MaWZldGltZS5HaXZpbmcpKQoKCmxpYnJhcnkoInJzYW1wbGUiKQoKZGF0YV9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGRhdGFjbGVhbiwgcHJvcCA9IDAuNzUpCgpkYXRhX3RyYWluIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQpCmRhdGFfdGVzdCA8LSB0ZXN0aW5nKGRhdGFfc3BsaXQpCgpgYGAKCgoKYGBge3J9CnAgPC0gZGF0YWNsZWFuICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDEwMCxieSA9IDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQoKZ2dwbG90bHkocCkKICAKcAoKZ2dwbG90KGRhdGEgPSBkYXRhY2xlYW4sIGFlcyh4ID0gQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0iYmx1ZSIpKyB4bGltKGMoMjAsMTAwKSkKCiAgCgoKYGBgCgpBbm90aGVyIEhpc3RvZ3JhbQoKCmBgYHtyfQoKZGF0YWNsZWFuICU+JQogIGZpbHRlcihBZ2UgPj0gMTApICU+JQogIGZpbHRlcihBZ2UgPD0gOTApICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIk92ZXJhbGwgRG9ub3IgQWdlIERpc3RyaWJ1dGlvbiIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTIwLDUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwLDIwMDApKSAKCmBgYApBZ2UgZGlzdHJpYnV0aW9uIGJ5IGdlbmRlciAKCmBgYHtyfQojQWdlIEdlbmRlciBmaWx0ZXJlZCBvdXQgYmVsb3cgMTUgYW5kIGFib3ZlIDkwIC0gYWxzbyByZW1vdmVkIFUgWCB0aGUgd2VpcmQgdmFsdWVzIApkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKEFnZSA+PSAxNSkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgbXV0YXRlKFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICBmaWx0ZXIoU2V4ICE9ICJVIikgJT4lCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gU2V4KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkKYGBgCgpEb25vciBhZ2UgZGlzdHJpYnV0aW9uIGJ5IG1hcml0YWwgc3RhdHVzIAoKYGBge3J9CiNBZ2UgTWFyaXRhbCBTdGF0dXMKZGF0YWNsZWFuICU+JQogIGZpbHRlcihBZ2UgPj0gMjApICU+JQogIGZpbHRlcihBZ2UgPD0gODUpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gTWFycmllZCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1KSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24gYnkgTWFyaXRhbCBTdGF0dXMiKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCw1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCw1MDAwMCwyMDAwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2ZmOTk3MyIsICIjMDBjZmNjIikpCmBgYAoKTGluZWFyIE1vZGVsIAoKYGBge3J9CiNUaGVzZSB3aWxsIGZvY3VzIG9uIHByZWRpY3Rpbmcgd2hldGhlciBhIGNvbnN0aXR1ZW50IGlzIGEgZG9ub3Igb3Igbm9uLWRvbm9yLiAKCgoKbW9kMWxtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiBNYXJyaWVkX3NpbXBsZSwKICAgICAgICAgICBkYXRhID0gZGF0YWNsZWFuKQoKbW9kMmxtIDwtIGxtKCBUb3RhbC5HaXZpbmcuWWVhcnMgfiBMaWZldGltZS5HaXZpbmcsCiAgICAgICAgICAgZGF0YSA9IGRhdGFjbGVhbikKCnN1bW1hcnkobW9kMWxtKQpzdW1tYXJ5KG1vZDJsbSkKI2luY3JlYXNpbmcgdGhlIGdpdmluZyB5ZWFyIG9uZSB5ZWFyIGluY3JlYXNlIHRvdGFsIGdpdmluZyBieSAwLjAwMzUKCgpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZykpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIGZhY2V0X3dyYXAofmRvbm9yc2VnX2ZjdCkgKyB0aGVtZV9jbGVhbihiYXNlX3NpemUgPSA4KSArIGxhYnMoeCA9ICJYIiwgeSA9ICJZIikgKwogICAgICBnZ3RpdGxlKCJUaXRsZSIpCgoKCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coSEguRmlyc3QuR2lmdC5BbW91bnQpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5kb25vcnNlZ19mY3QpICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiVGl0bGUiKQoKI1RoaXMgcGxvdCBhY3R1YWxseSBoYXMgc29tZSBpbnRlcmVzdGluZyByZXN1bHRzCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+Tm9fb2ZfQ2hpbGRyZW4pICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiVGl0bGUiKQoKCmRhdGFfdHJhaW4gJT4lIAogIHNlbGVjdF9pZihpcy5mYWN0b3IpICU+JSAKICBnbGltcHNlKCkKCgpgYGAKCgoKTU9SRSBNT0RFTFMKCkJpZyBsb2dpc3RpYyBtb2RlbAoKYGBge3J9CgojIFNldCBmYW1pbHkgdG8gYmlub21pYWwgdG8gc2V0IGxvZ2lzdGljIGZ1bmN0aW9uCiMgUnVuIHRoZSBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgc2V0Cgpkb25vcl9sb2dpdDEgPC0KICBnbG0oaGgubGlmZXRpbWUuZ2l2aW5nX2ZjdCB+IE1hcnJpZWRfc2ltcGxlLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQxKQoKCmRvbm9yX2xvZ2l0MiA8LQogIGdsbShoaC5saWZldGltZS5naXZpbmdfZmN0IH4gTm9fb2ZfQ2hpbGRyZW4sCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKc3VtbWFyeShkb25vcl9sb2dpdDIpCgoKCgoKCgojc3VtbWFyeShkYXRhX3RyYWluJG1ham9yX2dpZnRlcikKCmRvbm9yX2xvZ2l0MyA8LQogIGdsbShtYWpvcl9naWZ0ZXIgfiBNYXJyaWVkX3NpbXBsZSArIE5vX29mX0NoaWxkcmVuICsgZG9ub3JzZWdfc2ltcGxlICsgQXNzaWdubWVudF9mbGFnICsgVG90YWwuR2l2aW5nLlllYXJzLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQzKQpleHAoZG9ub3JfbG9naXQzJGNvZWZmaWNpZW50cykKCiN0cmFpbmluZyBwcmVkaWN0aW9ucyBmb3IgaW4gc2FtcGxlIHByZWRzIApwcmVkc190cmFpbiA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSAKCiN0ZXN0IHByZWRpY3RzIGZvciBPT1MgKG91dCBvZiBzYW1wbGUpCnByZWRzX3Rlc3QgPC0gcHJlZGljdChkb25vcl9sb2dpdDMsIG5ld2RhdGEgPSBkYXRhX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQoKaGVhZChwcmVkc190cmFpbikKaGVhZChwcmVkc190ZXN0KQoKCgpyZXN1bHRzX3RyYWluIDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdHJhaW4gICAlPiUgc2VsZWN0KG1ham9yX2dpZnRlcikgJT4lIAogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksCiAgYENsYXNzMWAgPSAgcHJlZHNfdHJhaW4sCiAgYHR5cGVgID0gcmVwKCJ0cmFpbiIsbGVuZ3RoKHByZWRzX3RyYWluKSkKKQoKcmVzdWx0c190ZXN0IDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdGVzdCAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgCiAgICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gYXMubnVtZXJpYyhtYWpvcl9naWZ0ZXIpKSwKICBgQ2xhc3MxYCA9ICBwcmVkc190ZXN0LAogIGB0eXBlYCA9IHJlcCgidGVzdCIsbGVuZ3RoKHByZWRzX3Rlc3QpKQopCgpyZXN1bHRzIDwtIGJpbmRfcm93cyhyZXN1bHRzX3RyYWluLHJlc3VsdHNfdGVzdCkKCmRpbShyZXN1bHRzX3RyYWluKQpkaW0ocmVzdWx0c190ZXN0KQpkaW0ocmVzdWx0cykKCmxpYnJhcnkoJ3Bsb3RST0MnKQoKcF9wbG90IDwtCiAgZ2dwbG90KHJlc3VsdHMsCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArCiAgZ2VvbV9yb2MobGFiZWxzaXplID0gMi41LAogICAgICAgICAgICNUb29rIHRoZSBsYWJlbHNpemUgZG93biB0byBhdm9pZCBjdXRvZmYKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKICNXZSByZW1vdmVkIHNvbWUgb2YgdGhlIGN1dG9mZnMgdG8gYXZvaWQgdGhlIG1hc2h1cCBuZWFyIHRoZSBvcmlnaW4uCgogICNDaGFuZ2VkIHRoZSB0aGVtZSB0byBhdm9pZCBjdXRvZmYgcGxvdCB2YWx1ZXMuCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAKICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIAogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArCiAgICAgIGdndGl0bGUoIlJPQyBQbG90OiBUcmFpbmluZyBhbmQgVGVzdCIpCnByaW50KHBfcGxvdCkgCgoKcF90cmFpbiA8LQogIGdncGxvdChyZXN1bHRzX3RyYWluLAogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKwogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgbGFicyh4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLCAKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQoKcF90ZXN0IDwtCiAgZ2dwbG90KHJlc3VsdHNfdGVzdCwKICAgICAgICAgYWVzKG0gPSBDbGFzczEsIGQgPSBtYWpvcl9naWZ0ZXIsIGNvbG9yID0gdHlwZSkpICsKICBnZW9tX3JvYyhsYWJlbHNpemUgPSAzLjUsCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArCiAKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArIAogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIpICsKICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikKCiNDYWxjdWxhdGluZyBBVUMgb2YgYm90aApwcmludChjYWxjX2F1YyhwX3RyYWluKSRBVUMpCnByaW50KGNhbGNfYXVjKHBfdGVzdCkkQVVDKQoKCgoKCgoKYGBgCgpSSURHRQoKYGBge3J9CgpsaWJyYXJ5KCdnbG1uZXQnKQpsaWJyYXJ5KCdnbG1uZXRVdGlscycpCgpyaWRnZV9maXQxIDwtIGN2LmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcgfiBzZXhfZmN0ICsgZG9ub3JzZWdfZmN0ICsgTm9fb2ZfQ2hpbGRyZW4sCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwKQoKI0FscGhhIDAgc2V0cyB0aGUgUmlkZ2UKcHJpbnQocmlkZ2VfZml0MSkKcHJpbnQocmlkZ2VfZml0MSRsYW1iZGEubWluKQoKcHJpbnQocmlkZ2VfZml0MSRsYW1iZGEuMXNlKQoKCmBgYAoKTEFTU08KCmBgYHtyfQoKI1VzaW5nIGN2LmdsbW5ldCBmcm9tIGNsYXNzCmxzKGRhdGFfdHJhaW4pIAppcy5mYWN0b3IoZGF0YV90cmFpbiRtYWpvcl9naWZ0ZXIpCmdsaW1wc2UoZGF0YV90cmFpbiRMaWZldGltZS5HaXZpbmcpCgpkYXRhX3RyYWluICU+JSAKICBzZWxlY3RfaWYoaXMuZmFjdG9yKSAlPiUgCiAgZ2xpbXBzZSgpCgoKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkoZ2xtbmV0VXRpbHMpCmxhc3NvX2ZpdCA8LSBjdi5nbG1uZXQoTGlmZXRpbWUuR2l2aW5nIH4gam9idGl0bGVfc2ltcGxlICsgZGVnMV9zaW1wbGUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICNBbHBoYSAxIGZvciBsYXNzbwogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkKCnByaW50KGxhc3NvX2ZpdCRsYW1iZGEubWluKQojCnByaW50KGxhc3NvX2ZpdCRsYW1iZGEuMXNlKQoKcGxvdChsYXNzb19maXQpCgpgYGAKCgoKCmBgYHtyfQoKY29lZihsYXNzb19maXQpCiNEZWZhdWx0IHNldHRpbmcgaXMgbGFtYmRhLjFzZQoKI0Zyb20gdGhlIGJvb2sgLSBzaG93aW5nIGNvbnZlcmdlbmNlIHdpdGggbGFtYmRhIHZhbHVlcwpwbG90KGxhc3NvX2ZpdCRnbG1uZXQuZml0LCB4dmFyPSJsYW1iZGEiKQphYmxpbmUodj1sb2coYyhsYXNzb19maXQkbGFtYmRhLm1pbiwgbGFzc29fZml0JGxhbWJkYS4xc2UpKSwgbHR5PTIpCgpgYGAKCmBgYHtyfQoKI2VuZXRfbW9kIDwtIGN2YS5nbG1uZXQoZGVwZW5kZW50IH4gaW5keTEgKyBpbmR5MiwKIyAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEsCiMgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gc2VxKDAsMSwgYnkgPSAwLjEpKQoKI3ByaW50KGVuZXRfbW9kKQojcGxvdChlbmV0X21vZCkKCgpgYGAKCkVMQVNUSUNORVQKCmBgYHtyIGVsYXN0aWNuZXR9CgptaW5sb3NzcGxvdChlbmV0X21vZCwgCiAgICAgICAgICAgIGN2LnR5cGUgPSAibWluIikKCmdldF9hbHBoYSA8LSBmdW5jdGlvbihmaXQpIHsKICBhbHBoYSA8LSBmaXQkYWxwaGEKICBlcnJvciA8LSBzYXBwbHkoZml0JG1vZGxpc3QsIAogICAgICAgICAgICAgICAgICBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQogIGFscGhhW3doaWNoLm1pbihlcnJvcildCn0KCmdldF9tb2RlbF9wYXJhbXMgPC0gZnVuY3Rpb24oZml0KSB7CiAgYWxwaGEgPC0gZml0JGFscGhhCiAgbGFtYmRhTWluIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgYFtbYCwgImxhbWJkYS5taW4iKQogIGxhbWJkYVNFIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgYFtbYCwgImxhbWJkYS4xc2UiKQogIGVycm9yIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgZnVuY3Rpb24obW9kKSB7bWluKG1vZCRjdm0pfSkKICBiZXN0IDwtIHdoaWNoLm1pbihlcnJvcikKICBkYXRhLmZyYW1lKGFscGhhID0gYWxwaGFbYmVzdF0sIGxhbWJkYU1pbiA9IGxhbWJkYU1pbltiZXN0XSwKICAgICAgICAgICAgIGxhbWJkYVNFID0gbGFtYmRhU0VbYmVzdF0sIGVyb3IgPSBlcnJvcltiZXN0XSkKfQoKYmVzdF9hbHBoYSA8LSBnZXRfYWxwaGEoZW5ldF9tb2QpCnByaW50KGJlc3RfYWxwaGEpCmdldF9tb2RlbF9wYXJhbXMoZW5ldF9tb2QpCgpiZXN0X21vZCA8LSBlbmV0X21vZCRtb2RsaXN0W1t3aGljaChlbmV0X21vZCRhbHBoYSA9PSBiZXN0X2FscGhhKV1dCgpwcmludChiZXN0X21vZCkKCgpgYGAKClJpZGdlcyBwbG90IC0gY291bGQgYmUgdXNlZnVsIGZvciBwbG90dGluZyBkb25hdGlvbnMgdnMgZG9ub3Igc2VnbWVudAoKYGBge3J9CgpsaWJyYXJ5KCdnZ3JpZGdlcycpCgpzdW1tYXJ5KGRhdGFfdHJhaW4kdmFyaWFibGUpCgpnZ3Bsb3QoZGF0YV90cmFpbiwgYWVzKHggPSBISC5MaWZldGltZS5HaXZpbmcsIHkgPSBkb25vcnNlZ19mY3QpKSArIGdlb21fZGVuc2l0eV9yaWRnZXMocmVsX21pbl9oZWlnaHQgPSAwLjAwNSkgKyB4bGltKGMoMCwgNDAwKSkgKyAKICAgICAgZ2d0aXRsZSgiSEggTGlmZXRpbWUgR2l2aW5nIGJ5IERvbm9yIFNlZ21lbnQiKQoKYGBgCgpgYGB7cn0KCmxpYnJhcnkoJ2NvcnJwbG90JykKCiNyZW1vdmluZyBJRCB6aXAgYW5kIG5vbm51bWVyaWMgCmNvcnJwbG90X2RhdGEgPC0gZGF0YWNsZWFuWy1jKDE6NDcsNTU6NTYsNTg6MTMwKV0KCiNDb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIG51bWVyaWMgZGF0YSB0eXBlCmNvbnZlcnRfZmFjMm51bSA8LSBmdW5jdGlvbih4KXsKICBhcy5udW1lcmljKGFzLmZhY3Rvcih4KSkKfQoKY29ycnBsb3RfZGF0YSA8LSBtdXRhdGVfYXQoY29ycnBsb3RfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgLnZhcnMgPSBjKDE6OCksCiAgICAgICAgICAgICAgICAgICAgIC5mdW5zID0gY29udmVydF9mYWMybnVtKQojbWFraW5nIGEgbWF0cml4CmNkX2NvciA8LSBjb3IoY29ycnBsb3RfZGF0YSkKCiNjcmVhdGluZyBjb3JyZWxhdGlvbgpjb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjQkI0NDAwIiwgIiNFRTk5OTAiLCAKICAiI0ZGRkZGRiIsICIjNzdBQUVFIiwgIiM0NDc3QkIiKSkKY29ycnBsb3QoY2RfY29yLCBtZXRob2Q9ImNvbG9yIiwgY29sPWNvbCgxMDApLAogIHR5cGU9Imxvd2VyIiwgYWRkQ29lZi5jb2wgPSAiYmxhY2siLAogIHRsLnBvcz0ibHQiLCB0bC5jb2w9ImJsYWNrIiwgCiAgdGwuY2V4PTAuNywgdGwuc3J0PTQ1LCAKICBudW1iZXIuY2V4PTAuNywKICBkaWFnPUZBTFNFKQoKYGBgCgoKUmFuZG9tIEZvcmVzdAoKYGBge3J9CgpsaWJyYXJ5KCdyYW5kb21Gb3Jlc3QnKQoKcmZfZml0X2Rvbm9yIDwtIHJhbmRvbUZvcmVzdChkZXBlbmRlbnQgfiAuLCAKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gY2xhc3NpZmljYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDcsCiAgICAgICAgICAgICAgICAgICAgICAgbmEuYWN0aW9uID0gbmEucm91Z2hmaXgsCiAgICAgICAgICAgICAgICAgICAgICAgbnRyZWUgPSAyMDAsCiAgICAgICAgICAgICAgICAgICAgICAgaW1wb3J0YW5jZT1UUlVFCiAgICAgICAgICAgICAgICAgICAgICAgKQoKcHJpbnQocmZfZml0X2Rvbm9yKQoKCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQoKdmFySW1wUGxvdChyZl9maXRfZG9ub3IsIHNvcnQgPSBUUlVFLCAKICAgICAgICAgICBuLnZhciA9IDUsCiAgICAgICAgICAgdHlwZSA9IDIsIGNsYXNzID0gTlVMTCwgc2NhbGUgPSBUUlVFLCAKICAgICAgICAgICBtYWluID0gZGVwYXJzZShzdWJzdGl0dXRlKHJmX2ZpdF9kb25vcikpKQoKCmBgYAoKYGBge3J9CgpsaWJyYXJ5KCdyYW5kb21Gb3Jlc3RFeHBsYWluZXInKQoKcGxvdF9taW5fZGVwdGhfZGlzdHJpYnV0aW9uKAogIHJmX2ZpdF9kb25vciwKICBrID0gMTAsCiAgbWluX25vX29mX3RyZWVzID0gMCwKICBtZWFuX3NhbXBsZSA9ICJ0b3BfdHJlZXMiLAogIG1lYW5fc2NhbGUgPSBGQUxTRSwKICBtZWFuX3JvdW5kID0gMiwKICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBtaW5pbWFsIGRlcHRoIGFuZCBpdHMgbWVhbiIKKQoKYGBgCgo=